import os
import yaml
import json
import requests
import re
from bs4 import BeautifulSoup
from loguru import logger
from backend.database.db import db_manager
import os
from dotenv import load_dotenv

class ToolCaller:
    _instance = None
    _initialized = False

    def __new__(cls):
        if cls._instance is None:
            cls._instance = super(ToolCaller, cls).__new__(cls)
        return cls._instance

    def __init__(self):
        if self._initialized:
            return
        
        # 加载.env文件
        load_dotenv()
        
        # 加载配置
        config_path = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))), 'configs', 'config.yaml')
        with open(config_path, 'r', encoding='utf-8') as f:
            config = yaml.safe_load(f)
            self.tools_config = config.get('tools', {}) or {}
        
        # 注册可用工具
        self.available_tools = {
            'web_search': self.web_search,
            'database_query': self.database_query,
            'file_operation': self.file_operation
        }
        
        self._initialized = True
    
    async def call_tool(self, tool_name, parameters, timeout=None, trace_id=None):
        """调用指定的工具"""
        try:
            # 检查工具是否可用
            if not bool(self.tools_config.get('enabled', False)):
                return {'success': False, 'error': '工具调用功能未启用'}
            
            # 检查工具是否在允许的列表中
            allowed = self.tools_config.get('allowed_tools') or list(self.available_tools.keys())
            if tool_name not in allowed:
                return {'success': False, 'error': f'不允许的工具: {tool_name}'}
            
            # 检查工具是否存在
            if tool_name not in self.available_tools:
                return {'success': False, 'error': f'工具不存在: {tool_name}'}
            
            # 调用工具
            logger.info(f"调用工具: {tool_name}, 参数: {parameters}")
            tool_func = self.available_tools[tool_name]
            # 简化：直接同步调用。若后续需要避免阻塞，可改为 asyncio.to_thread
            result = tool_func(parameters)
            
            logger.info(f"工具调用完成: {tool_name}, 结果: {result}")
            return result
        except Exception as e:
            logger.error(f"工具调用失败: {str(e)}")
            return {'success': False, 'error': str(e)}
    
    def web_search(self, parameters):
        """执行网络搜索"""
        try:
            # 检查功能是否启用
            if not self.tools_config.get('web_search', {}).get('enabled', False):
                return {'success': False, 'error': '网络搜索功能未启用'}
            
            # 验证参数
            if 'query' not in parameters:
                return {'success': False, 'error': '缺少查询参数'}
            
            query = parameters['query']
            max_results = parameters.get('max_results', 3)
            
            logger.info(f"执行网络搜索: {query}, 最大结果数: {max_results}")
            
            # 获取搜索引擎配置
            search_engine = self.tools_config.get('web_search', {}).get('search_engine', 'google')
            api_key = self.tools_config.get('web_search', {}).get('api_key', '')
            
            # 从环境变量获取API密钥作为备用
            if not api_key:
                if search_engine == 'google':
                    api_key = os.getenv('GOOGLE_API_KEY', '')
                elif search_engine == 'bing':
                    api_key = os.getenv('BING_API_KEY', '')
            
            if not api_key:
                # 如果没有API密钥，使用模拟数据
                logger.warning("未配置搜索引擎API密钥，使用模拟数据")
                search_results = []
                for i in range(max_results):
                    search_results.append({
                        'title': f'搜索结果 {i+1}: {query}',
                        'url': f'https://example.com/search?q={query.replace(" ", "+")}&result={i+1}',
                        'snippet': f'这是关于{query}的搜索结果摘要。请配置搜索引擎API密钥以获取真实搜索结果。',
                        'source': '模拟搜索引擎'
                    })
            else:
                # 调用真实的搜索引擎API
                search_results = self._call_search_engine_api(search_engine, api_key, query, max_results)
            
            return {
                'success': True,
                'data': {
                    'query': query,
                    'results': search_results
                }
            }
        except Exception as e:
            logger.error(f"网络搜索失败: {str(e)}")
            return {'success': False, 'error': str(e)}
    
    def database_query(self, parameters):
        """执行数据库查询"""
        try:
            # 检查功能是否启用
            if not (self.tools_config.get('database_query', {}) or {}).get('enabled', False):
                return {'success': False, 'error': '数据库查询功能未启用'}
            
            # 验证参数
            if 'query' not in parameters:
                return {'success': False, 'error': '缺少查询语句'}
            
            if 'database' not in parameters:
                return {'success': False, 'error': '缺少数据库名称'}
            
            # 检查数据库是否在允许的列表中
            allowed_dbs = (self.tools_config.get('database_query', {}) or {}).get('allowed_databases', [])
            if parameters['database'] not in allowed_dbs:
                return {'success': False, 'error': f'不允许访问的数据库: {parameters["database"]}'}
            
            query = parameters['query']
            database = parameters['database']
            
            # 安全检查，防止SQL注入
            if not self._is_safe_sql_query(query):
                return {'success': False, 'error': '不安全的SQL查询'}
            
            logger.info(f"执行数据库查询: {query}, 数据库: {database}")
            
            # 执行查询
            with db_manager.get_cursor() as cursor:
                cursor.execute(query)
                
                # 获取列名
                columns = [desc[0] for desc in cursor.description]
                
                # 获取结果
                results = []
                for row in cursor.fetchall():
                    results.append(dict(zip(columns, row)))
                
                return {
                    'success': True,
                    'data': {
                        'query': query,
                        'columns': columns,
                        'results': results,
                        'row_count': len(results)
                    }
                }
        except Exception as e:
            logger.error(f"数据库查询失败: {str(e)}")
            return {'success': False, 'error': str(e)}
    
    def file_operation(self, parameters):
        """执行文件操作"""
        try:
            # 检查功能是否启用
            if not (self.tools_config.get('file_operation', {}) or {}).get('enabled', False):
                return {'success': False, 'error': '文件操作功能未启用'}
            
            # 验证参数
            if 'operation' not in parameters:
                return {'success': False, 'error': '缺少操作类型'}
            
            if 'path' not in parameters:
                return {'success': False, 'error': '缺少文件路径'}
            
            operation = parameters['operation']
            path = parameters['path']
            
            # 检查路径是否在允许的范围内
            if not self._is_path_allowed(path):
                return {'success': False, 'error': f'不允许访问的路径: {path}'}
            
            logger.info(f"执行文件操作: {operation}, 路径: {path}")
            
            # 执行相应的文件操作
            if operation == 'read':
                with open(path, 'r', encoding='utf-8') as f:
                    content = f.read()
                return {
                    'success': True,
                    'data': {
                        'operation': operation,
                        'path': path,
                        'content': content
                    }
                }
            elif operation == 'list':
                import os
                if not os.path.exists(path):
                    return {'success': False, 'error': f'路径不存在: {path}'}
                
                items = os.listdir(path)
                file_info = []
                for item in items:
                    item_path = os.path.join(path, item)
                    file_info.append({
                        'name': item,
                        'type': 'directory' if os.path.isdir(item_path) else 'file',
                        'size': os.path.getsize(item_path) if os.path.isfile(item_path) else None,
                        'modified_at': os.path.getmtime(item_path)
                    })
                
                return {
                    'success': True,
                    'data': {
                        'operation': operation,
                        'path': path,
                        'items': file_info,
                        'item_count': len(file_info)
                    }
                }
            else:
                return {'success': False, 'error': f'不支持的文件操作: {operation}'}
        except Exception as e:
            logger.error(f"文件操作失败: {str(e)}")
            return {'success': False, 'error': str(e)}
    
    def _is_safe_sql_query(self, query):
        """检查SQL查询是否安全，防止SQL注入"""
        # 这里实现简单的SQL注入防护
        # 实际应用中应该使用参数化查询
        
        # 禁止危险的SQL语句
        dangerous_keywords = [
            'DROP', 'TRUNCATE', 'ALTER', 'DELETE', 'UPDATE',
            'INSERT', 'CREATE', 'RENAME', 'GRANT', 'REVOKE',
            'EXEC', 'EXECUTE', 'xp_cmdshell', 'sp_', ';', '--'
        ]
        
        # 将查询转换为大写进行比较
        query_upper = query.upper()
        
        # 检查是否包含危险关键字
        for keyword in dangerous_keywords:
            if keyword in query_upper:
                logger.warning(f"检测到危险的SQL关键字: {keyword}")
                return False
        
        return True
    
    def _is_path_allowed(self, path):
        """检查文件路径是否在允许的范围内"""
        # 获取绝对路径
        abs_path = os.path.abspath(path)
        
        # 检查是否在允许的路径列表中
        allowed_paths = []
        if isinstance(self.tools_config, dict):
            if 'allowed_paths' in self.tools_config:
                allowed_paths = self.tools_config['allowed_paths']
            elif 'file_operation' in self.tools_config and 'allowed_paths' in self.tools_config['file_operation']:
                allowed_paths = self.tools_config['file_operation']['allowed_paths']
        
        for allowed_path in allowed_paths:
            allowed_abs_path = os.path.abspath(allowed_path)
            if abs_path.startswith(allowed_abs_path):
                return True
        
        logger.warning(f"路径不在允许的范围内: {path}")
        return False
    
    def _call_search_engine_api(self, search_engine, api_key, query, max_results):
        """调用真实的搜索引擎API"""
        try:
            search_results = []
            
            if search_engine == 'google':
                # Google Search API 调用
                import requests
                cx = os.getenv('GOOGLE_CX', '')
                if not cx:
                    logger.warning("未配置Google搜索引擎ID(GOOGLE_CX)")
                    # 如果没有CX，返回模拟数据
                    for i in range(max_results):
                        search_results.append({
                            'title': f'Google搜索结果 {i+1}: {query}',
                            'url': f'https://google.com/search?q={query.replace(" ", "+")}&start={i}',
                            'snippet': f'这是{query}的Google搜索结果摘要。请配置GOOGLE_CX环境变量以获取真实搜索结果。',
                            'source': 'Google搜索(模拟)'
                        })
                    return search_results
                
                url = f"https://www.googleapis.com/customsearch/v1?key={api_key}&cx={cx}&q={query}&num={max_results}"
                response = requests.get(url, timeout=10)
                response.raise_for_status()  # 如果请求失败，抛出异常
                data = response.json()
                
                # 处理搜索结果
                if 'items' in data:
                    for item in data['items'][:max_results]:
                        search_results.append({
                            'title': item.get('title', ''),
                            'url': item.get('link', ''),
                            'snippet': item.get('snippet', ''),
                            'source': 'Google Search'
                        })
                
            elif search_engine == 'bing':
                # Bing Search API 调用
                import requests
                url = f"https://api.bing.microsoft.com/v7.0/search?q={query}&count={max_results}"
                headers = {'Ocp-Apim-Subscription-Key': api_key}
                response = requests.get(url, headers=headers, timeout=10)
                response.raise_for_status()  # 如果请求失败，抛出异常
                data = response.json()
                
                # 处理搜索结果
                if 'webPages' in data and 'value' in data['webPages']:
                    for item in data['webPages']['value'][:max_results]:
                        search_results.append({
                            'title': item.get('name', ''),
                            'url': item.get('url', ''),
                            'snippet': item.get('snippet', ''),
                            'source': 'Bing Search'
                        })
                
            else:
                logger.warning(f"不支持的搜索引擎: {search_engine}")
                # 返回模拟数据
                for i in range(max_results):
                    search_results.append({
                        'title': f'搜索结果 {i+1}: {query}',
                        'url': f'https://example.com/search?q={query.replace(" ", "+")}&result={i+1}',
                        'snippet': f'这是关于{query}的搜索结果摘要。',
                        'source': f'{search_engine}(模拟)'
                    })
            
            return search_results
        except Exception as e:
            logger.error(f"搜索引擎API调用失败: {str(e)}")
            # 如果API调用失败，返回模拟数据
            fallback_results = []
            for i in range(max_results):
                fallback_results.append({
                    'title': f'搜索结果 {i+1}: {query}',
                    'url': f'https://example.com/search?q={query.replace(" ", "+")}&result={i+1}',
                    'snippet': f'搜索引擎API调用失败，这是{query}的模拟结果摘要。错误: {str(e)}',
                    'source': '模拟数据'
                })
            return fallback_results
    
    def get_available_tools(self):
        """获取所有可用的工具"""
        tools_info = []
        allowed = self.tools_config.get('allowed_tools') or list(self.available_tools.keys())
        for tool_name in allowed:
            if tool_name in self.available_tools:
                # 获取工具的描述和参数信息
                # 这里简化实现，实际应用中可以更详细地描述每个工具
                tools_info.append({
                    'name': tool_name,
                    'description': self._get_tool_description(tool_name),
                    'parameters': self._get_tool_parameters(tool_name),
                    'enabled': self._is_tool_enabled(tool_name)
                })
        
        return tools_info
    
    def _get_tool_description(self, tool_name):
        """获取工具的描述"""
        descriptions = {
            'web_search': '执行网络搜索，获取相关信息',
            'database_query': '执行数据库查询，获取数据',
            'file_operation': '执行文件操作，如读取文件、列出目录等'
        }
        return descriptions.get(tool_name, '未定义的工具')
    
    def _get_tool_parameters(self, tool_name):
        """获取工具的参数信息"""
        parameters_info = {
            'web_search': [
                {'name': 'query', 'type': 'string', 'required': True, 'description': '搜索查询词'},
                {'name': 'max_results', 'type': 'integer', 'required': False, 'description': '最大结果数', 'default': 3}
            ],
            'database_query': [
                {'name': 'query', 'type': 'string', 'required': True, 'description': 'SQL查询语句'},
                {'name': 'database', 'type': 'string', 'required': True, 'description': '数据库名称'}
            ],
            'file_operation': [
                {'name': 'operation', 'type': 'string', 'required': True, 'description': '操作类型，如read、list'},
                {'name': 'path', 'type': 'string', 'required': True, 'description': '文件或目录路径'}
            ]
        }
        return parameters_info.get(tool_name, [])
    
    def _is_tool_enabled(self, tool_name):
        """检查工具是否启用"""
        if tool_name == 'web_search':
            return (self.tools_config.get('web_search', {}) or {}).get('enabled', False)
        elif tool_name == 'database_query':
            return (self.tools_config.get('database_query', {}) or {}).get('enabled', False)
        elif tool_name == 'file_operation':
            return (self.tools_config.get('file_operation', {}) or {}).get('enabled', False)
        return False

# 全局工具调用器实例
tool_caller = ToolCaller()